/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Driver interaction with extended Linux CFG8021
*/

#include "driver_nl80211_sprd.h"

static void wpa_driver_notify_country_change(void *ctx, char *cmd) {
    if ((os_strncasecmp(cmd, "COUNTRY", 7) == 0) ||
        (os_strncasecmp(cmd, "SETBAND", 7) == 0)) {
        union wpa_event_data event;
        os_memset(&event, 0, sizeof(event));
        event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
        if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
            event.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
            if (os_strlen(cmd) > 9)
            {
                event.channel_list_changed.alpha2[0] = cmd[8];
                event.channel_list_changed.alpha2[1] = cmd[9];
            }
        } else {
            event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
        }
        wpa_supplicant_event(ctx, EVENT_CHANNEL_LIST_CHANGED, &event);
    }
}

int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
                                  size_t buf_len) {
    struct i802_bss *bss = priv;
    struct wpa_driver_nl80211_data *drv = bss->drv;
    struct wpa_supplicant *wpa_s = (struct wpa_supplicant*)(drv->ctx);
    struct ifreq ifr;
    android_wifi_priv_cmd priv_cmd;
    int ret = 0;
    char cmd_buf[MAX_DRV_CMD_SIZE];
    union wpa_event_data event_data;

    memset(&ifr, 0, sizeof(ifr));
    memset(&priv_cmd, 0, sizeof(priv_cmd));
    memset(&event_data, 0, sizeof(event_data));
    wpa_printf(MSG_INFO, "%s, bss ifname : %s", __func__, bss->ifname);
   /* if (strstr(bss->ifname, P2P_MGMT_DEVICE_PREFIX)) {
        char ifprop[PROPERTY_VALUE_MAX];
        if (property_get("wifi.interface", ifprop, "wlan0") != 0) {
            wpa_printf(MSG_INFO, "convert to '%s' interface", ifprop);
            os_strlcpy(ifr.ifr_name, ifprop, IFNAMSIZ);
        }
    } else {*/
        os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
   // }
    ifr.ifr_name[IFNAMSIZ - 1] = '\0';

    if (os_strcasecmp(cmd, "START") == 0) {
        linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
        wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
    } else if (os_strcasecmp(cmd, "STOP") == 0) {
        linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
        wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
    } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
        u8 macaddr[ETH_ALEN] = {};
        ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr);
        if (!ret) {
            ret = os_snprintf(buf, buf_len, "Macaddr = " MACSTR, MAC2STR(macaddr));
            wpa_printf(MSG_INFO, "%s, cmd : %s, return " MACSTR, __func__, cmd, MAC2STR(macaddr));
        } else {
            wpa_printf(MSG_ERROR, "%s, cmd : %s, ret : %d", __func__, cmd, ret);
        }
    } else if (os_strncasecmp(cmd, "MIRACAST", 8) == 0) {
        struct driver_cmd_msg *miracast;
        int value = atoi(cmd + 8);

        memset(buf, 0, buf_len);
        miracast = (struct driver_cmd_msg *)buf;
        miracast->msg_type = CMD_SUBTYPE_MIRACAST_MODE;
        miracast->msg_len = sizeof(int);
        memcpy(miracast->msg_data, &value, sizeof(value));

        priv_cmd.buf = (u64)(uintptr_t)miracast;
        priv_cmd.used_len = sizeof(*miracast) + miracast->msg_len;
        priv_cmd.total_len = priv_cmd.used_len;
        ifr.ifr_data = &priv_cmd;
        if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 2, &ifr)) < 0) {
            wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s, errno: %s",
                       __func__, cmd, strerror(errno));
        } else {
            wpa_printf(MSG_INFO, "driver cmd '%s' success", cmd);
        }
    } else if (os_strncasecmp(cmd, "P2PMACADDR", 10) == 0) {
        os_memset(cmd_buf, 0, sizeof(cmd_buf));
        os_memcpy(cmd_buf, cmd, 11 + ETH_ALEN); //The strlen may be mistake if the mac contains '0x0' char.

        priv_cmd.buf = (u64)(uintptr_t)cmd_buf;
        priv_cmd.used_len = sizeof(cmd_buf);
        priv_cmd.total_len = sizeof(cmd_buf);
        ifr.ifr_data = &priv_cmd;

        if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 6, &ifr)) < 0) {
            wpa_printf(MSG_ERROR, "failed to issue private command P2PMACADD");
        } else {
            wpa_printf(MSG_INFO, "driver cmd P2PMACADDR " MACSTR " success", MAC2STR(cmd + 11));
        }
    } else if (os_strncasecmp(cmd, "SETSUSPENDMODE", 14) == 0) {
        os_memset(cmd_buf, 0, sizeof(cmd_buf));
        os_strlcpy(cmd_buf, cmd, strlen(cmd) + 1);

        priv_cmd.buf = (u64)(uintptr_t)cmd_buf;
        priv_cmd.used_len = sizeof(cmd_buf);
        priv_cmd.total_len = sizeof(cmd_buf);
        ifr.ifr_data = &priv_cmd;

        if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 4, &ifr)) < 0) {
            wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s, errno: %s",
                       __func__, cmd, strerror(errno));
        } else {
            wpa_printf(MSG_INFO, "driver cmd '%s' success", cmd);
        }
    } else if (os_strncasecmp(cmd, "SIGMA_MODE ", 11) == 0) {
        char *pos = strchr(cmd, ' ');
        if (pos == NULL)
            return -1;
        int sigma = atoi(++pos);
        global_sigma_mode = sigma;
        wpa_printf(MSG_INFO, "set sigma mode: %d: 0x%p", sigma, wpa_s->global);
    } else if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
        wpa_driver_notify_country_change(drv->ctx, cmd);
    } else {
        os_memcpy(buf, cmd, strlen(cmd) + 1);

        priv_cmd.buf = (u64)(uintptr_t)buf;
        priv_cmd.used_len = buf_len;
        priv_cmd.total_len = buf_len;
        ifr.ifr_data = &priv_cmd;

        if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) {
            wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s, errno: %s",
                       __func__, cmd, strerror(errno));
        } else {
            wpa_printf(MSG_INFO, "driver cmd '%s' success", cmd);
        }
    }
    return ret;
}

int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) {
    wpa_printf(MSG_DEBUG, "%s: NOT IMPLETE", __func__);

    return 0;
}

int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) {
    wpa_printf(MSG_DEBUG, "%s: NOT IMPLETE", __func__);

    /* Return 0 till we handle p2p_presence request completely in the driver */
    return 0;
}

int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) {
    wpa_printf(MSG_DEBUG, "%s: NOT IMPLETE", __func__);

    return 0;
}

int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
                                 const struct wpabuf *proberesp,
                                 const struct wpabuf *assocresp) {
    wpa_printf(MSG_DEBUG, "%s: NOT IMPLETE", __func__);

    return 0;
}

int wpa_driver_nl80211_driver_cmd_wnm(void *priv, enum wnm_oper oper,
                                      const u8 *peer, u8 *buf, u16 *buf_len) {
    struct i802_bss *bss = priv;
    struct wpa_driver_nl80211_data *drv = bss->drv;
    struct ifreq ifr;
    android_wifi_priv_cmd priv_cmd;
    int ret = 0;
    char buff[MAX_DRV_CMD_SIZE];

    wpa_printf(MSG_DEBUG, "wnm_oper: %d", oper);
    memset(buff, 0, sizeof(buff));
    memset(&ifr, 0, sizeof(ifr));
    memset(&priv_cmd, 0, sizeof(priv_cmd));

    os_memcpy(buf, "WNM_SLEEP", 9);
    buf[9] = ' ';
    buf[10] = oper;
    os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);

    priv_cmd.buf = (u64)(uintptr_t)buff;
    priv_cmd.used_len = sizeof(buff);
    priv_cmd.total_len = sizeof(buff);
    ifr.ifr_data = &priv_cmd;

    if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) {
        wpa_printf(MSG_ERROR, "%s: failed to issue private command, error: %s", __func__, strerror(errno));
    } else {
        wpa_printf(MSG_DEBUG, "%s %s len = %d, %zu", __func__, buf, ret, strlen((char *)buf));
    }

    return ret;
}
